#include "host_config.h"
#include "host_buf.h"
#include "papi.h"

#include "bt_ctrl.h"
#include "bt_msg.h"

#include "gatt.h"
#include "hid.h"

#include "smp.h"
#include "manager.h"

#define HIDS_MAX_CLIENT_CC				5	
#define HIDS_BOOT_KB_INPUT_VALUE_LEN	5

#define HIDS_BCDHID		0x1011

#define HID_MAX_CONN	2

enum
{
	HIDS_DISCONNECTED = 0,
	HIDS_FAST_CONNECTABLE,
	HIDS_SLOW_CONNECTABLE,
	HIDS_CONNECTED,
};

typedef struct 
{
	//u_int16 bcdHID;
	//u_int8  bCountryCode;
	//u_int8  flags;
	u_int8	value[4];
}hids_info_t;

typedef struct
{
	u_int16 aclHandle;
	u_int16 address;
	u_int32 secFlags;
	u_int8 state;
	u_int8 advTimerId;
}hids_t;

static hids_t g_hidDevice;


//battery service
static u_int8 hids_battery_uuid[2] = {ATT_UUID_BATTERY&0xff, ATT_UUID_BATTERY>>8};

static gatt_chara_def_short_t hids_char_bl = {ATT_CHARA_PROP_READ, 0, 0, ATT_UUID_BATTERY_LEVEL&0xff, ATT_UUID_BATTERY_LEVEL>>8};
static u_int8 hids_bl;

static const gatt_element_t hids_battery[] =
{
	{2, ATT_PM_READABLE, ATT_UUID_PRIMARY, ATT_FMT_SHORT_UUID|ATT_FMT_GROUPED, (void*)hids_battery_uuid, NULL}, //primary service declaration
	
	{sizeof(hids_bl), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&hids_bl, NULL},
};

//HID Service
#define ATT_HID_UUID				0x1812
#define HID_UUID_PROTOCOL			0x2A4E
#define HID_UUID_REPORT				0x2A4D
#define HID_UUID_REPORT_MAP			0x2A4B
#define HID_UUID_BOOT_KB_INPUT		0x2A22
#define HID_UUID_BOOT_KB_OUTPUT		0x2A32
#define HID_UUID_BOOT_MOUSE_INPUT	0x2A33
#define HID_UUID_INFORMATION		0x2A4A
#define HID_UUID_CONTROL_POINT		0x2A4C


enum 
{
	BOOT_MODE = 0,
	REPORT_MODE,
};

static const u_int8 hids_uuid[2] = {ATT_HID_UUID&0xff, ATT_HID_UUID>>8};

static gatt_chara_def_short_t char_protocol = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_WWP, 0, 0, HID_UUID_PROTOCOL&0xff, HID_UUID_PROTOCOL>>8};
static u_int8 protocol_mode = REPORT_MODE;

static gatt_chara_def_short_t char_kbin_report = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_WRITE|ATT_CHARA_PROP_NOTIFY, 0, 0, HID_UUID_REPORT&0xff, HID_UUID_REPORT>>8};
static char kbin_report_value[8];
static gatt_chara_ccb_t  kbin_report_cc[HIDS_MAX_CLIENT_CC];
static u_int8 kbin_report_ref[2] = {1, 0x01};

static gatt_chara_def_short_t char_kbout_report = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_WRITE|ATT_CHARA_PROP_WWP, 0, 0, HID_UUID_REPORT&0xff, HID_UUID_REPORT>>8};
static char kbout_report_value[8];
static u_int8 kbout_report_ref[2] = {2, 0x02};

static gatt_chara_def_short_t char_kb_map = {ATT_CHARA_PROP_READ, 0, 0, HID_UUID_REPORT_MAP&0xff, HID_UUID_REPORT_MAP>>8};
static const u_int8 kb_map_value[] =
{
	0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 
    0x25, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 
    0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 
    0x00, 0x29, 0xff, 0x81, 0x00, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 
    0x95, 0x08, 0x09, 0xe9, 0x09, 0xea, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x81, 0x02, 
    0xc0, 0x05, 0x01, 0x09, 0x80, 0xa1, 0x01, 0x85, 0x03, 0x19, 0x81, 0x29, 0x83, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 
    0x01, 0x81, 0x02, 0x95, 0x05, 0x81, 0x03, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x04, 0x15, 0x00, 0x25, 0x01, 
    0x75, 0x01, 0x95, 0x18, 0x09, 0xb5, 0x09, 0xb6, 0x09, 0xb7, 0x09, 0xcd, 0x09, 0xe2, 0x09, 0xe5, 0x09, 0xe7, 0x09, 0xe9, 
    0x09, 0xea, 0x0a, 0x52, 0x01, 0x0a, 0x53, 0x01, 0x0a, 0x54, 0x01, 0x0a, 0x55, 0x01, 0x0a, 0x83, 0x01, 0x0a, 0x8a, 0x01, 
    0x0a, 0x92, 0x01, 0x0a, 0x94, 0x01, 0x0a, 0x21, 0x02, 0x0a, 0x23, 0x02, 0x0a, 0x24, 0x02, 0x0a, 0x25, 0x02, 0x0a, 0x26, 
    0x02, 0x0a, 0x27, 0x02, 0x0a, 0x2a, 0x02, 0x81, 0x02, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x05, 0x05, 0x01, 
    0x09, 0x06, 0xa1, 0x02, 0x05, 0x06, 0x09, 0x20, 0x15, 0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x08, 0x81, 0x00, 0x09, 0x20, 
    0x15, 0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x08, 0xb1, 0x00, 0xc0, 0xc0, 
    0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x06, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, 0x00, 
    0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 
    0x75, 0x08, 0x95, 0x03, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x75, 0x08, 0x95, 0x01, 0x15, 0x81, 0x25, 0x7f, 0x81, 
    0x06, 0xc0, 0xc0,
    0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0xff, 
    0x05, 0x06, 0x95, 0x01, 0x75, 0x02, 0x19, 0x24, 0x29, 0x26, 0x81, 0x02, 0x75, 0x06, 0x81, 0x01, 0xc0
};

static gatt_chara_def_short_t char_kb_boot_input = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_INDICATE, 0, 0, HID_UUID_BOOT_KB_INPUT&0xff, HID_UUID_BOOT_KB_INPUT>>8};
static u_int8 kb_boot_input_value[6];
static gatt_chara_ccb_t kb_boot_input_cc[HIDS_MAX_CLIENT_CC];

static gatt_chara_def_short_t char_kb_boot_output = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_WRITE|ATT_CHARA_PROP_WWP, 0, 0, HID_UUID_BOOT_KB_OUTPUT&0xff, HID_UUID_BOOT_KB_INPUT>>8};
static u_int8 kb_boot_output_value[6];

static gatt_chara_def_short_t char_mouse_boot_input = {ATT_CHARA_PROP_READ|ATT_CHARA_PROP_INDICATE, 0, 0, HID_UUID_BOOT_MOUSE_INPUT&0xff, HID_UUID_BOOT_MOUSE_INPUT>>8};
static u_int8 mouse_boot_input_value[6];
static gatt_chara_ccb_t mouse_boot_input_cc[HIDS_MAX_CLIENT_CC];

static gatt_chara_def_short_t char_hid_info = {ATT_CHARA_PROP_READ, 0, 0, HID_UUID_INFORMATION&0xff, HID_UUID_INFORMATION>>8};
static hids_info_t hids_info ={HIDS_BCDHID&0xff, HIDS_BCDHID>>8, 0x00, 0x03};

static gatt_chara_def_short_t char_hid_control = {ATT_CHARA_PROP_WWP, 0, 0, HID_UUID_CONTROL_POINT&0xff, HID_UUID_CONTROL_POINT>>8};
static u_int8 hids_control = 0x01;

void hids_value_cb(void *param)
{

}

static  gatt_element_t hids_service[] =
{
	{2, ATT_PM_READABLE, ATT_UUID_PRIMARY, ATT_FMT_SHORT_UUID|ATT_FMT_GROUPED, (void*)hids_uuid, NULL},

	 //protocol mode characteristic
	{sizeof(char_protocol), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_protocol, NULL},
	{sizeof(protocol_mode), ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_PROTOCOL, ATT_FMT_WRITE_NOTIFY|ATT_FMT_SHORT_UUID|ATT_FMT_FIXED_LENGTH, (void*)&protocol_mode, hids_value_cb},

	 //keyboard input report characteristic
	{sizeof(char_kbin_report), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_kbin_report, NULL},
	{0, ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_REPORT, ATT_FMT_SHORT_UUID, (void*)kbin_report_value, NULL},
	{sizeof(kbin_report_cc), ATT_PM_READABLE|ATT_PM_WRITEABLE, ATT_UUID_CLIENT, ATT_FMT_SHORT_UUID|ATT_FMT_WRITE_NOTIFY|ATT_FMT_FIXED_LENGTH, (void*)kbin_report_cc, hids_value_cb},
	{sizeof(kbin_report_ref), ATT_PM_READABLE, ATT_UUID_REPORT_REF, ATT_FMT_SHORT_UUID, (void*)kbin_report_ref, NULL},

	 //keyboard out report characteristic
	{sizeof(char_kbout_report), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_kbout_report, NULL},
	{0, ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_REPORT, ATT_FMT_SHORT_UUID|ATT_FMT_WRITE_NOTIFY, (void*)kbout_report_value, NULL},
	{sizeof(kbout_report_ref), ATT_PM_READABLE, ATT_UUID_REPORT_REF, ATT_FMT_SHORT_UUID, (void*)kbout_report_ref, NULL},

	//keyboard report map 
	{sizeof(char_kb_map), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_kb_map, NULL},
	{sizeof(kb_map_value), ATT_PM_READABLE, HID_UUID_REPORT_MAP, ATT_FMT_SHORT_UUID, (void*)kb_map_value, NULL},
	{sizeof(hids_battery_uuid), ATT_PM_READABLE, ATT_UUID_EXTERNAL_REF, ATT_FMT_SHORT_UUID, (void*)hids_battery_uuid, NULL},

	//boot keyboard input report
	{sizeof(char_kb_boot_input), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_kb_boot_input, NULL},
	{HIDS_BOOT_KB_INPUT_VALUE_LEN, ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_BOOT_KB_INPUT, ATT_FMT_SHORT_UUID, (void*)kb_boot_input_value, NULL},
	{sizeof(kb_boot_input_cc), ATT_PM_READABLE|ATT_PM_WRITEABLE, ATT_UUID_CLIENT, ATT_FMT_SHORT_UUID|ATT_FMT_WRITE_NOTIFY|ATT_FMT_FIXED_LENGTH, (void*)kb_boot_input_cc, hids_value_cb},

	//boot keyboard output report
	{sizeof(char_kb_boot_output), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_kb_boot_output, NULL},
	{0, ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_BOOT_KB_OUTPUT, ATT_FMT_SHORT_UUID|ATT_FMT_WRITE_NOTIFY, (void*)kb_boot_output_value, hids_value_cb},

	//boot mouse input report
	{sizeof(char_mouse_boot_input), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_mouse_boot_input, NULL},
	{HIDS_BOOT_KB_INPUT_VALUE_LEN, ATT_PM_READABLE|ATT_PM_WRITEABLE, HID_UUID_BOOT_MOUSE_INPUT, ATT_FMT_SHORT_UUID, (void*)mouse_boot_input_value, NULL},
	{sizeof(mouse_boot_input_cc), ATT_PM_READABLE|ATT_PM_WRITEABLE, ATT_UUID_CLIENT, ATT_FMT_SHORT_UUID|ATT_FMT_WRITE_NOTIFY|ATT_FMT_FIXED_LENGTH, (void*)mouse_boot_input_cc, hids_value_cb},

	//HID information characteristic
	{sizeof(char_hid_info), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_hid_info, NULL},
	{sizeof(hids_info), ATT_PM_READABLE, HID_UUID_INFORMATION, ATT_FMT_SHORT_UUID, (void*)&hids_info, NULL},

	//HID control point characteristic
	{sizeof(char_hid_control), ATT_PM_READABLE, ATT_UUID_CHAR, ATT_FMT_SHORT_UUID, (void*)&char_hid_control, NULL},
	{sizeof(hids_control), ATT_PM_WRITEABLE|ATT_PM_READABLE, HID_UUID_CONTROL_POINT, ATT_FMT_SHORT_UUID, (void*)&hids_control, NULL},
};

void hids_add_service(u_int8 isKeyBoard)
{
	//gatt_add_service(hids_battery, sizeof(hids_battery)/sizeof(gatt_element_t));
	gatt_add_service(hids_service, sizeof(hids_service)/sizeof(gatt_element_t));
}

u_int16 aclHandle;

void hids_sendInputReport(t_bdaddr address, u_int8* data, u_int16 length)
{
	u_int8 handle[8];
	gatt_server_req_t req;

	req.aclHandle = aclHandle;
	req.data = data;
	req.length = length;
	req.reqCode = GATT_INDICATION;//GATT_NOTIFICATION;

	if(protocol_mode == REPORT_MODE)
	{
		hids_service[4].length = length;
		req.attHandle = BT_READ_LE_UINT16(char_kbin_report.value) -1;
		pMemcpy(hids_service[4].attValue, data, length);
	}
	else
	{
		req.attHandle = BT_READ_LE_UINT16(char_kb_boot_input.value+1) -1;
		pMemcpy(hids_service[13].attValue, data, length);
	}

	//BT_WRITE_LE_UINT16(handle, 0x0001);
	//BT_WRITE_LE_UINT16(handle+4, 0xffff);
	//req.attHandle = 0x0010;
	//req.data = handle;
	//req.length = 8;
	gatt_server_req(&req);
}

static void hids_adv_timeout_cb(void *handle)
{
	g_hidDevice.advTimerId = 0;
	if(g_hidDevice.state == HIDS_FAST_CONNECTABLE)
	{
		g_hidDevice.state = HIDS_SLOW_CONNECTABLE;
		MGR_LESetAdvIntval(0x0030, 0x0050);
		g_hidDevice.advTimerId = rdabt_timer_create(30*100, hids_adv_timeout_cb, (void*)0, RDABT_TIMER_ONESHOT);
	}
	else
	{
		g_hidDevice.state = HIDS_DISCONNECTED;
		MGR_LESetAdvertMode(0, 0, 0);
	}
}

static u_int8 hids_is_device_bonded(t_bdaddr address)
{

	return 0;
}

void hids_enter_connectable(t_bdaddr address)
{
	u_int32 interval = 0;
	MGR_LESetAdvIntval(0x0020, 0x0030);

	if(hids_is_device_bonded(address))
	{
		g_hidDevice.state = HIDS_FAST_CONNECTABLE;
		MGR_LESetAdvertMode(1, 2, 0);
		interval = 30*100;
	}
	else
	{
		g_hidDevice.state = HIDS_SLOW_CONNECTABLE;
		MGR_LESetAdvertMode(LE_LIMITEDDISCOVERY_MODE, ADV_IND, 0);
		interval = 180*100;
	}
	//g_hidDevice.advTimerId = rdabt_timer_create(interval, hids_adv_timeout_cb, (void*)0, RDABT_TIMER_ONESHOT);
}

void gatt_sample_db4_add_service();
APIDECL1 t_api APIDECL2 hids_start_server()
{
	int index = 0;
	t_bdaddr address;
	u_int8 adv[31];

	u_int8 hids_dn[] = "rdabt";

	//add dynamicly
	hids_init();

	hids_add_service(1);
	//gatt_sample_db4_add_service();

	adv[index++] = 0x03;
	adv[index++] = 0x16;
	BT_WRITE_LE_UINT16(adv+index, ATT_HID_UUID);
	index += 2;

#if 0 
	adv[index++] = sizeof(hids_dn)+1;
	adv[index++] = 0x09;
	pMemcpy(adv+index, hids_dn, sizeof(hids_dn));
	index += sizeof(hids_dn);
#endif

#if 0 
	adv[index++] = 0x03;
	adv[index++] = 0x19;
	BT_WRITE_LE_UINT16(adv+index, hids_appearance);
#endif

#if 1
	//SERVICE SOLICITATION
	adv[index++] = 0x03;
	adv[index++] = 0x14;
	BT_WRITE_LE_UINT16(adv+index, ATT_HID_UUID);
	index += 2; 
#endif

#if 0
	//manufacture specific data
	adv[index++] = 0x03;
	adv[index++] = 0xff;
	adv[index++] = 0xA0;
	adv[index++] = 0xA0;
#endif

#if 0
	//tx power level
	adv[index++] = 0x02;
	adv[index++] = 0x0A;
	adv[index++] = 0xA0;
#endif 

#if 1
	//SLAVE CONNECTION INTERVAL RANGE 
	adv[index++] = 0x05;
	adv[index++] = 0x12;
	BT_WRITE_LE_UINT16(adv+index, 0x0006);
	index += 2;
	BT_WRITE_LE_UINT16(adv+index, 0x0c80);
	index += 2;
#endif

	MGR_LESetAdvData(adv, index);

	pMemset(&address, 0, sizeof(address));
	hids_enter_connectable(address);

	return 0;
}

APIDECL1 t_api APIDECL2 hids_DisconnectLink()
{
	MGR_LEReleaseACL(aclHandle);
}



APIDECL1 t_api APIDECL2 hids_DiscoveryName()
{
	u_int8 uuid[2];
	BT_WRITE_LE_UINT16(uuid, ATT_UUID_DEVICENAME);
#if GATT_CLIENT_SUPPORT == 1
	{
		int att_read_by_type_req(u_int16 cid, u_int16 start_handle, u_int16 end_handle, u_int8* uuid, u_int8 uuidFmt);
		att_read_by_type_req(0x0004, 0x0001, 0xffff, uuid, 1);
	}
	
#endif
}

int hids_process_msg(u_int16 msgId, void *data)
{
	switch(msgId)
	{
	case GATT_CONNECT_CNF:
		{
			u_int8 auth = 0;
			gatt_connect_cnf_t *msg = (gatt_connect_cnf_t*)data;
			t_hid_connect conn;
			t_bdaddr address = {0x0};

			if(g_hidDevice.advTimerId != 0)
			{
				rdabt_cancel_timer(g_hidDevice.advTimerId);
				g_hidDevice.advTimerId = 0;
			}
			//SMP_SecRequest(auth);
			
			aclHandle = msg->aclHandle;
			conn.result = msg->result;
			hids_sendInputReport(address, &auth, 1);
			RDABT_Send_Message(GATT_HIDP_CONNECT_CNF, RDABT_GATT_HIDS, RDABT_ADP, sizeof(conn), &conn);
		}
		break;
	case GATT_DISCONNECT_IND:
		{
			extern u_int8 gatt_is_privacy_enabled();
			extern int gatt_get_reconn_address(t_bdaddr *address);
			t_bdaddr address;

			gatt_get_reconn_address(&address);

			if(gatt_is_privacy_enabled())
			{
				MGR_SetOwnAddrType(0x01);
				MGR_SetRandomAddress(address);
				MGR_LESetAdvertMode(LE_LIMITEDDISCOVERY_MODE, ADV_IND, 0);
			}
			else
			{
				t_bdaddr address = {0xd3, 0x19, 0x00, 0x66, 0x88, 0x99};
				MGR_SetOwnAddrType(0x0);
				//MGR_LESetDirectAddr(0x0, address);
				
				MGR_LESetAdvertMode(LE_LIMITEDDISCOVERY_MODE, ADV_IND, 0);
			}
		}
		break;
	default:
		break;
	}
}

int hids_init()
{
	pMemset(&g_hidDevice, 0, sizeof(g_hidDevice));

	gatt_init_server(RDABT_GATT_HIDS, hids_process_msg);
	return 0;
}

t_api rdabt_gatt_hids_layer_ctrl(u_int8 flags)
{
	if(flags&(RDABT_HOST_STARTUP|RDABT_HOST_RESET)) 
	{

		//hids_add_service(1);
	}
	return 0;
}

t_api rdabt_gatt_hids_msg_dispatch(rdabt_msg_t *message)
{
	return hids_process_msg(message->msg_id, message->data);
}




